/*
	The MIT License

	Copyright (c) 2008 IFMO/GameDev Studio

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

#if 0

#include "maya_local.h"





/*-----------------------------------------------------------------------------
	General stuff :
-----------------------------------------------------------------------------*/

//
//	EMShaderBase::EMShaderBase
//
EMShaderBase::EMShaderBase(void)
{
	TRACE_API_CALLS("");
	attachSceneCallbacks();
	
	LoadShader();
}


//
//	EMShaderBase::~EMShaderBase
//
EMShaderBase::~EMShaderBase(void)
{
	TRACE_API_CALLS("");
	detachSceneCallbacks();
}


//
//	EMShaderBase::postConstructor
//
void EMShaderBase::postConstructor(void)
{
	TRACE_API_CALLS("");
	setMPSafe(false);
}


//
//	EMShaderBase::releaseEverything
//	- is called by 'releaseCallback', which handles Maya events
//
void EMShaderBase::releaseEverything(void)
{
}


/*-----------------------------------------------------------------------------
	Some callbacks :
-----------------------------------------------------------------------------*/

void EMShaderBase::releaseCallback(void* clientData)
{
	EMShaderBase *pThis = (EMShaderBase*) clientData;
	pThis->releaseEverything();
}


void EMShaderBase::attachSceneCallbacks()
{
	fBeforeNewCB				= MSceneMessage::addCallback( MSceneMessage::kBeforeNew,				releaseCallback, this);
	fBeforeOpenCB				= MSceneMessage::addCallback( MSceneMessage::kBeforeOpen,				releaseCallback, this);
	fBeforeRemoveReferenceCB	= MSceneMessage::addCallback( MSceneMessage::kBeforeRemoveReference,	releaseCallback, this);
	fMayaExitingCB				= MSceneMessage::addCallback( MSceneMessage::kMayaExiting,				releaseCallback, this);
}																							


void EMShaderBase::detachSceneCallbacks()
{
	if (fBeforeNewCB)				MMessage::removeCallback(fBeforeNewCB);
	if (fBeforeOpenCB)				MMessage::removeCallback(fBeforeOpenCB);
	if (fBeforeRemoveReferenceCB)	MMessage::removeCallback(fBeforeRemoveReferenceCB);
	if (fMayaExitingCB)			 	MMessage::removeCallback(fMayaExitingCB);

	fBeforeNewCB = 0;
	fBeforeOpenCB = 0;
	fBeforeRemoveReferenceCB = 0;
	fMayaExitingCB = 0;
}


/*-----------------------------------------------------------------------------
	Attribute stuff :
-----------------------------------------------------------------------------*/

MObject	EMShaderBase::a_diffuse;
MObject	EMShaderBase::a_specular;
MObject	EMShaderBase::a_normal;
MObject	EMShaderBase::a_glow;

void * EMShaderBase::creator()
{
	TRACE_API_CALLS("creator");
    return new EMShaderBase();
}


MStatus EMShaderBase::initialize()
{
	MStatus	status;

	TRACE_API_CALLS("initialize");
    MFnNumericAttribute nAttr; 
    MFnTypedAttribute tAttr;

    // Create input attributes
	MAYA_CHECK( MA_AddColorTexture( a_diffuse,	"diffuse",  "diff", EVec4(0.5, 0.5, 0.5, 0) ) );	//	diffuse color	
	MAYA_CHECK( MA_AddColorTexture( a_specular,	"specular", "spec", EVec4(0.5, 0.1, 1.0, 0) ) );	//	k_spec, roughness, k_metal,	
	MAYA_CHECK( MA_AddColorTexture( a_normal,	"normal",	"norm", EVec4(1.0, 0.5, 1.0, 0) ) );	//	nx, ny, nz	
	MAYA_CHECK( MA_AddColorTexture( a_glow,		"glow",		"glow", EVec4(0.0, 0.0, 0.0, 0) ) );	//	glow color	

	//	Affect 'outColor' :
	MAYA_CHECK( attributeAffects( a_diffuse,	outColor ) );
	MAYA_CHECK( attributeAffects( a_specular,	outColor ) );
	MAYA_CHECK( attributeAffects( a_normal,		outColor ) );
	MAYA_CHECK( attributeAffects( a_glow,		outColor ) );

    return MS::kSuccess;
}


//
//	EMShaderBase::compute
//
MStatus EMShaderBase::compute(const MPlug& plug, MDataBlock& block ) 
{ 
	TRACE_API_CALLS("compute");

    if ((plug != outColor) && (plug.parent() != outColor))
		return MS::kUnknownParameter;

	MFloatVector & color  = block.inputValue( a_diffuse ).asFloatVector();

    // set output color attribute
    MDataHandle outColorHandle = block.outputValue( outColor );
    MFloatVector& outColor = outColorHandle.asFloatVector();
	outColor = color;

    outColorHandle.setClean();
    return MS::kSuccess;
}



/*-----------------------------------------------------------------------------
	Material rendering stuff (OpenGL)
-----------------------------------------------------------------------------*/

//
//	EMShaderBase::glBind
//
MStatus	EMShaderBase::glBind(const MDagPath&)
{
	TRACE_API_CALLS("glBind");
	
	SetupShader();

	return MS::kSuccess;
}


//
//	EMShaderBase::glUnbind
//
MStatus	EMShaderBase::glUnbind(const MDagPath&)
{
	TRACE_API_CALLS("glUnbind");

	ResetShader();

	return MS::kSuccess;
}


//
//	EMShaderBase::SetupShader
//
void EMShaderBase::SetupShader(void)
{
	MStatus	status;

	glPushAttrib ( GL_ALL_ATTRIB_BITS ); // This is overkill
	glDisable ( GL_LIGHTING );
	glDisable ( GL_LIGHT0 );
	glEnable ( GL_DEPTH_TEST );

	EVec4 color;
	MObject texture_mobj;
	
	MayaCheck( MA_GetColorTexture(thisMObject(), a_diffuse, color, texture_mobj) );
	
	if (texture_mobj==MObject::kNullObj) {
		glDisable(GL_TEXTURE_2D);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color.v);
		
	} else {
		EVec4	diff_color(1,1,1,1);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diff_color.v);
		
		MImageFileInfo::MHwTextureType hwType;
		
		if (MS::kSuccess == MHwTextureManager::glBind( texture_mobj, hwType )) {
			glEnable(GL_TEXTURE_2D);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
	}

	//shader->SetPixelShader(0);
}


//
//	EMShaderBase::ResetShader
//
void EMShaderBase::ResetShader(void)
{
	glPopAttrib();
}


//
//	EMShaderBase::glGeometry
//
MStatus	EMShaderBase::glGeometry(const MDagPath & path,
		int prim,			unsigned int writable,
		int indexCount,		const unsigned int * indexArray,
		int vertexCount,	const int * vertexIDs,		const float * vertexArray,
		int normalCount,	const float ** normalArrays,
		int colorCount,		const float ** colorArrays,
		int texCoordCount,	const float ** texCoordArrays)
{
	TRACE_API_CALLS("glGeometry");
	MStatus stat = MStatus::kSuccess;

	if ( prim != GL_TRIANGLES && prim != GL_TRIANGLE_STRIP)	{
        return MS::kFailure;
    }


		glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );
		// GL_VERTEX_ARRAY does not necessarily need to be
		// enabled, as it should be enabled before this routine
		// is valled.
		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );

		glVertexPointer		( 3, GL_FLOAT, 0, &vertexArray[0] );
		glNormalPointer		(    GL_FLOAT, 0, &normalArrays[0][0] );
		
		if (texCoordCount && texCoordArrays[0]) {
			glEnableClientState( GL_TEXTURE_COORD_ARRAY );
			glTexCoordPointer( 2, GL_FLOAT, 0, &texCoordArrays[0][0] );
		}
		
		glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );
		
		// The client attribute is already being popped. You
		// don't need to reset state here.
		//glDisableClientState( GL_NORMAL_ARRAY );
		//glDisableClientState( GL_VERTEX_ARRAY );
		glPopClientAttrib();

	return stat;
}


//
//	EMShaderBase::renderSwatchImage
//
MStatus EMShaderBase::renderSwatchImage( MImage & outImage )
{
	MStatus status = MStatus::kFailure;

	// Get the hardware renderer utility class
	MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
	if (pRenderer)
	{
		const MString& backEndStr = pRenderer->backEndString();

		// Get geometry
		// ============
		unsigned int* pIndexing = 0;
		unsigned int  numberOfData = 0;
		unsigned int  indexCount = 0;

		MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere;

		MGeometryData* pGeomData =	pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount );
		
		if( !pGeomData ) {
			return MStatus::kFailure;
		}

		// Make the swatch context current
		// ===============================
		//
		unsigned int width, height;
		outImage.getSize( width, height );
		unsigned int origWidth = width;
		unsigned int origHeight = height;

		MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr, width, height );

		if( status2 == MS::kSuccess )
		{
			glPushAttrib ( GL_ALL_ATTRIB_BITS );

			// Get camera
			// ==========
			{
				// Get the camera frustum from the API
				double l, r, b, t, n, f;
				pRenderer->getSwatchOrthoCameraSetting( l, r, b, t, n, f );

				glMatrixMode(GL_PROJECTION);
				glLoadIdentity();
				glOrtho( l, r, b, t, n, f );

				glMatrixMode(GL_MODELVIEW);
				glLoadIdentity();

				glRotatef( 45, 1.0, 1.0, 1.0 );
			}

			// Draw The Swatch
			// ===============
			drawSwatch( pGeomData, pIndexing, numberOfData, indexCount );

			// Read pixels back from swatch context to MImage
			// ==============================================
			pRenderer->readSwatchContextPixels( backEndStr, outImage );

			// Double check the outing going image size as image resizing
			// was required to properly read from the swatch context
			outImage.getSize( width, height );
			if (width != origWidth || height != origHeight)
			{
				status = MStatus::kFailure;
			}
			else
			{
				status = MStatus::kSuccess;
			}

			glPopAttrib();
		}

		// Release data references
		pRenderer->dereferenceGeometry( pGeomData, numberOfData );
	}
	return status;
}


//
//	EMShaderBase::drawTheSwatch
//	
void EMShaderBase::drawSwatch( MGeometryData* pGeomData,
			unsigned int* pIndexing,
			unsigned int  numberOfData,
			unsigned int  indexCount )
{
	TRACE_API_CALLS("drawTheSwatch");

	MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
	if( !pRenderer )	return;

	// Get the default background color (does not work?)
	float r, g, b, a;
	MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a );
	glClearColor( 0.35f, 0.35f, 0.35f, 0 );

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	SetupShader();

	// Draw default geometry
	{
		if (pGeomData)
		{
			glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );

			float *vertexData = (float *)( pGeomData[0].data() );
			if (vertexData)	{
				glEnableClientState( GL_VERTEX_ARRAY );
				glVertexPointer ( 3, GL_FLOAT, 0, vertexData );
			}

			float *normalData = (float *)( pGeomData[1].data() );
			if (normalData)	{
				glEnableClientState( GL_NORMAL_ARRAY );
				glNormalPointer (    GL_FLOAT, 0, normalData );
			}

			float *texcoordData = (float *)( pGeomData[2].data() );
			if (texcoordData)	{
				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer ( 2,   GL_FLOAT, 0, texcoordData );
			}

			if (vertexData && normalData && pIndexing ) {
				glDrawElements ( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, pIndexing );
			}

			glPopClientAttrib();
		}
	}

	ResetShader();
}


/*-----------------------------------------------------------------------------
	Attribute stuff :
	
	Make sure that all attributes are cached internal for
	optimal performance !
-----------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------
	Main rendering stuff :
-----------------------------------------------------------------------------*/
#define SET_SHADER_DEFINE(def)	shader->SetDefine(def, #def)
const uint64_t	DIFFUSE_SRC_COLOR		=	0x00000001;
const uint64_t	DIFFUSE_SRC_TEXTURE		=	0x00000002;
const uint64_t	SPECULAR_SRC_COLOR		=	0x00000004;
const uint64_t	SPECULAR_SRC_TEXTURE	=	0x00000008;
const uint64_t	NORMAL_SRC_COLOR		=	0x00000010;
const uint64_t	NORMAL_SRC_TEXTURE		=	0x00000020;

//
//	EMShaderBase::LoadShader
//
void EMShaderBase::LoadShader(void) 
{
	shader	=	shader_system->RegisterShader("E:/IFMO_GAME_1/mission/_maya/shaders/test.cg");
	
	SET_SHADER_DEFINE( DIFFUSE_SRC_COLOR	);
	SET_SHADER_DEFINE( DIFFUSE_SRC_TEXTURE	);
	SET_SHADER_DEFINE( SPECULAR_SRC_COLOR	);
	SET_SHADER_DEFINE( SPECULAR_SRC_TEXTURE	);
	SET_SHADER_DEFINE( NORMAL_SRC_COLOR		);
	SET_SHADER_DEFINE( NORMAL_SRC_TEXTURE	);
	
	shader->SetPixelShader(0, true);
}


//
//	EMShaderBase::normalsPerVertex
//
int	EMShaderBase::normalsPerVertex()
{
	//	DHG : tangents???
	return 3;
}


//
//	EMShaderBase::texCoordsPerVertex
//
int	EMShaderBase::texCoordsPerVertex()
{
	return 1;
}


//
//	EMShaderBase::getTexCoordSetNames
//
int	EMShaderBase::getTexCoordSetNames(MStringArray& names)
{
	return 0;
}

#endif